package com.itheima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.common.constants.BusinessConstants;
import com.itheima.common.enums.HttpCodeEnum;
import com.itheima.common.exception.LeadNewsException;
import com.itheima.common.util.RequestContextUtil;
import com.itheima.common.vo.PageResultVo;
import com.itheima.wemedia.dto.WmNewsContentNodeDto;
import com.itheima.wemedia.dto.WmNewsDtoSave;
import com.itheima.wemedia.dto.WmNewsPageReqDto;
import com.itheima.wemedia.mapper.WmMaterialMapper;
import com.itheima.wemedia.mapper.WmNewsMaterialMapper;
import com.itheima.wemedia.mapper.WmUserMapper;
import com.itheima.wemedia.pojo.WmMaterial;
import com.itheima.wemedia.pojo.WmNews;
import com.itheima.wemedia.mapper.WmNewsMapper;
import com.itheima.wemedia.pojo.WmNewsMaterial;
import com.itheima.wemedia.pojo.WmUser;
import com.itheima.wemedia.service.WmNewsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.wemedia.vo.WmNewsVo;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @description <p>自媒体图文内容信息 业务实现</p>
 *
 * @version 1.0
 * @package com.itheima.wemedia.service.impl
 */
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmMaterialMapper wmMaterialMapper;

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Autowired
    private WmUserMapper wmUserMapper;
     /**
     * 1.文章分页查询
     *
     * @param dto
     * @return
     */

    @Override
    public PageResultVo<WmNews> findPage(WmNewsPageReqDto dto) {
        //1. 设置分页参数
        IPage<WmNews> pageInfo = new Page<>(dto.getPage(),dto.getSize());

        //2. 构建查询条件
        LambdaQueryWrapper<WmNews> queryWrapper = Wrappers.<WmNews>lambdaQuery();

        //2.1 查询自己发布的文章
        queryWrapper.eq(WmNews::getUserId, RequestContextUtil.getLoginUserId());

        // 通过状态查询
        queryWrapper.eq(null!=dto.getStatus(),WmNews::getStatus,dto.getStatus());

        // 关键字，标题模糊查询
        queryWrapper.like(StringUtils.isNotEmpty(dto.getKeyword()),WmNews::getTitle,dto.getKeyword());

        // 频道
        queryWrapper.eq(null!=dto.getChannelId(),WmNews::getChannelId,dto.getChannelId());

        // 发布的开始时间 >=
        queryWrapper.ge(null!=dto.getBeginPubDate(),WmNews::getPublishTime,dto.getBeginPubDate());

        // 发布时间 结束时间 <=
        queryWrapper.le((null!=dto.getEndPubDate()),WmNews::getPublishTime,dto.getEndPubDate());

        //3. 查询
        page(pageInfo,queryWrapper);

        //4. 构建结果返回
        return PageResultVo.pageResult(dto.getPage(),dto.getSize(), pageInfo.getTotal(), pageInfo.getRecords());

    }

    /**
     * 2.发表文章
     *
     * @param draft
     * @param dtoSave
     * @return
     */
    @Override
    public Long submit(boolean draft, WmNewsDtoSave dtoSave) {
        LocalDateTime now = LocalDateTime.now();
        // 存储封面图片
        List<String> coverImageList = new ArrayList<>();
        // 存储内容图片
        List<String> contentImageList = new ArrayList<>();

        // 参数校验:
        // 文章内容
        String content = dtoSave.getContent();
        if(null == dtoSave || StringUtils.isEmpty(content)){
            throw new LeadNewsException("文章内容不能为空!");
        }

        //1. 添加数据到文章表wm_news
        WmNews pojo = new WmNews();

        // 复制对象中的属性：属性名称且类型相同时才会复制
        // 把p1的属性值复制给p2, p1与p2都不能为空，否则NP
        BeanUtils.copyProperties(dtoSave,pojo);
        // 如果没有填发布时间，当下时间来发布，第十天优化
        if(null==pojo.getPublishTime()){
            pojo.setPublishTime(now);
        }
        // 发布者
        pojo.setUserId(Integer.parseInt(RequestContextUtil.getLoginUserId()));
        //pojo.setCreatedTime(now); //在做修改与更新功能时，把这句代码放到下面去了。
        pojo.setSubmitedTime(now);

        // 是否是草稿
        //Boolean isDraft = Boolean.valueOf(draft); 这里本来就是boolean,没必要转boolean //换成正则表达式表示
        pojo.setStatus(draft ? BusinessConstants.WmNewsConstants.STATUS_DRAFT : BusinessConstants.WmNewsConstants.STATUS_SUBMIT);

        // 刚刚创建的文章，没上架
        pojo.setEnable(BusinessConstants.WmNewsConstants.ENABLE_OFF);

        // 文章封面, 如果是自动的。dto提交过来的images是空的
        if (CollectionUtils.isNotEmpty(dtoSave.getImages())){
            // 有图片，且是多图时，以逗号分割拼接
            //String.join(",", dtoSave.getImages()), 把参数2集合,拼接成字符串，每个元素以参数1（,）分割
            String images = String.join(",", dtoSave.getImages());
            pojo.setImages(images);
            // 添加封面图片
            coverImageList.addAll(dtoSave.getImages());
        }

        //从文章内容中抽取图片作为封面图片
        List<WmNewsContentNodeDto> contentNodeList = JSON.parseArray(content,WmNewsContentNodeDto.class);
        //List<String> collect = contentNodeList.stream()
        //    .filter(node -> BusinessConstants.WmNewsConstants.CONTENT_IMAGE.equals(node.getType()))
        //    .map(WmNewsContentNodeDto::getValue).collect(Collectors.toList());
        // 抽取图片: 遍历contentNodeList, 如果type="image", 提取value
        // 内容中的图片
        for (WmNewsContentNodeDto node : contentNodeList) {
            // 这个节点就是图片
            if (BusinessConstants.WmNewsConstants.CONTENT_IMAGE.equals(node.getType())){
                String imageUrl = node.getValue();
                contentImageList.add(imageUrl);
            }
        }
        if (dtoSave.getType().intValue() == BusinessConstants.WmNewsConstants.TYPE_AUTO){
            // 封面类型为自动
            /**
             * -1 是自动: 从文章内容中抽取图片作为封面图片
             *    文章中没图片：type=0
             *    图片小于2张: type=1
             *    图片超过2张: type=3
             */
            int size = contentImageList.size();
            //文章中没图片：type=0
            if (size == 0){
                pojo.setType(BusinessConstants.WmNewsConstants.TYPE_NONE_IMG);
                // 没图，封面就空白
                pojo.setImages("");
            }else if (size <= 2){
                //图片小于2张: type=1
                pojo.setType(BusinessConstants.WmNewsConstants.TYPE_SINGLE_IMG);
                // 添加封面图片
                pojo.setImages(contentImageList.get(0));
                coverImageList.add(contentImageList.get(0));
            }else{
                //图片超过2张: type=3
                pojo.setType(BusinessConstants.WmNewsConstants.TYPE_MULTIPLE_IMG);

                //String images = contentImageList.stream().limit(3).collect(Collectors.joining(","));
                // 取前3张图片   limit 0,size, 循环3次
                StringJoiner joiner = new StringJoiner(",");
                //String images = "";
                for (int i = 0; i < 3; i++) {
                    //images+=contentImageList.get(i) + ",";
                    joiner.add(contentImageList.get(i));
                    coverImageList.add(contentImageList.get(i));
                }
                String images = joiner.toString();
                // 设置文章的封面图片
                pojo.setImages(images);
            }
        }
        // 判断是否有id,如果有id代表修改
        if (null==dtoSave.getId()){
            // 保存到数据
            pojo.setCreatedTime(now);
            save(pojo);
        }else {
            // 修改
            updateById(pojo);
            // 删除原有素材(图片)与文章的关系
            removeNewsMaterialRelationship(pojo.getId());
        }
        //2. 添加文章与素材的关系
        //2.1 获取封面图片list, 内容图片list
        saveWmNewsMaterialRelationship(pojo.getId(),coverImageList,contentImageList);
        // 判断提交的类型,如果是提交审核，发消息给kafka
        if(pojo.getStatus() == BusinessConstants.WmNewsConstants.STATUS_SUBMIT){
            // 发消息给kafka，消息内容为文章的id
            kafkaTemplate.send(BusinessConstants.MqConstants.WM_NEWS_AUTO_SCAN_TOPIC,pojo.getId().toString());
        }
        return pojo.getId();
    }

    /**
     * 3.通过id删除文章
     * 条件：文章不为发布状态或不为上架状态
     *
     * @param newsId
     */
    @Override
    @Transactional  //事务控制
    public void deleteById(Long newsId) {
        //1. 通过id查询文章
        WmNews wmNews = getById(newsId);
        //2. 判断文章状态
        //3. 如果状态为已发布(9),或已上架(enable=1)，则不能删除
        if (wmNews.getStatus() == BusinessConstants.WmNewsConstants.STATUS_PUBLISHED
            || wmNews.getEnable() == BusinessConstants.WmNewsConstants.ENABLE_ON){
            throw new LeadNewsException("文章已发布或已上架，不能删除!");
        }
        //4. 否则可以删除 删除文章
        removeById(wmNews);
        //5. 删除文章与素材关系
        removeNewsMaterialRelationship(newsId);
        //6. 事务控制

    }

    /**
     * 4.文章上架与下架处理
     *
     * @param paramMap
     */
    @Override
    public void downOrUp(Map<String, Object> paramMap) {
        // 参数校验, newsId不为空，enable不为空, enable必须是0,1其中1个
        Long newsId = Long.parseLong((String)paramMap.get("id"));
        Integer operation = (Integer)paramMap.get("enable");
        // 参数校验
        if (newsId==null || null==operation || operation.intValue()<0 || operation.intValue()>1){
            throw new LeadNewsException(HttpCodeEnum.PARAM_INVALID);
        }
        //1. 通过id查询文章
        WmNews news = getById(newsId);
        //2. 只有文章已发布，才可以做上下架的操作
        if (news.getStatus()!=BusinessConstants.WmNewsConstants.STATUS_PUBLISHED){
            throw new LeadNewsException("文章处于非发布状态，不能做上|下架操作");
        }
        //3. 当enable=0下架操作, 把enable更新为0, 且更新status=1
        WmNews updatePojo = new WmNews();  //之所以要单独new一个对象出来，是因为需要单独更新某个字段，而不改变其他字段。
        updatePojo.setId(newsId);
        if (operation == 0){
            // 修改为下架
            updatePojo.setEnable(BusinessConstants.WmNewsConstants.ENABLE_OFF);
            //更新status=1 待审核
            updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_SUBMIT);
            updatePojo.setSubmitedTime(LocalDateTime.now());
        }else{
            //4. 当enable=1上架操作。把enable更新为1即可
            updatePojo.setEnable(BusinessConstants.WmNewsConstants.ENABLE_ON);
        }
        // 更新记录
        if(updateById(updatePojo)){
            // 发消息给kafka，文章微服同步上下架的状态
            // 消息内容articleId, 上架或下架的值
            Map<String,Object> msg = new HashMap<>();
            msg.put("articleId",news.getArticleId());

            // enable 在自媒体端: 0:下架，1：上架
            // 在文章微服务的文章配置中: is_down 0:上架，1：下架
            msg.put("enable",updatePojo.getEnable()==0?1:0);
            kafkaTemplate.send(BusinessConstants.MqConstants.WM_NEWS_UP_OR_DOWN_TOPIC,JSON.toJSONString(msg));
        }

    }


    /**
     * 5.给后台管理用的文章分页查询
     *
     * @param dto
     * @return
     */
    @Override
    public PageResultVo<WmNewsVo> findPage4Admin(WmNewsPageReqDto dto) {
        //1. 设置分页参数
        IPage<WmNews> pageInfo = new Page<>(dto.getPage(),dto.getSize());
        //2. 构建查询条件
        LambdaQueryWrapper<WmNews> queryWrapper = Wrappers.<WmNews>lambdaQuery();
        // 通过状态查询
        queryWrapper.eq(null!=dto.getStatus(),WmNews::getStatus,dto.getStatus());
        // 关键字，标题模糊查询
        queryWrapper.like(StringUtils.isNotEmpty(dto.getTitle()),WmNews::getTitle,dto.getTitle());
        // 排序
        queryWrapper.orderByDesc(WmNews::getSubmitedTime);
        //3. 查询
        page(pageInfo,queryWrapper);
        //4. 获取分页结果
        List<WmNews> newsList = pageInfo.getRecords();
        List<WmNewsVo> voList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(newsList)){
            //5. 补全作者名称
            //5.1. 取出所有作者id
            List<Integer> userIds = newsList.stream().map(WmNews::getUserId).collect(Collectors.toList());
            //5.2 查询Wm_user表
            List<WmUser> wmUsers = wmUserMapper.selectBatchIds(userIds);
            // Function.identity()表示stream流中的元素本身对象
            Map<Integer,WmUser> userInfoMap = wmUsers.stream().collect(Collectors.toMap(WmUser::getId, Function.identity()));
            voList = newsList.stream().map(wmNews -> WmNewsVo
                    .build(wmNews,userInfoMap.get(wmNews.getUserId())))
                    .collect(Collectors.toList());  //这一句代码的作用等同于下面一段代码。
            /*Map<Integer, WmUser> userInfoMap = new HashMap<>();
            for (WmUser wmUser : wmUsers) {
                userInfoMap.put(wmUser.getId(), wmUser);
            }
            //6. 把pojo转成vo
            for (WmNews wmNews : newsList) {
                // 文章作者id
                Integer userId = wmNews.getUserId();
                WmUser wmUser = userInfoMap.get(userId);// 通过id获取用户对象
                WmNewsVo vo = WmNewsVo.build(wmNews, wmUser);
                voList.add(vo);
            }*/
        }
        return PageResultVo.pageResult(dto.getPage(),dto.getSize(), pageInfo.getTotal(),voList);
    }

    /**
     * 6.人工审核-驳回
     *
     * @param paramMap
     */
    @Override
    public void authFail(Map<String, Object> paramMap) {
        //1. 获取文章id
        Long newsId = (Long) paramMap.get("id");
        //2. 驳回的原因
        String reason = (String) paramMap.get("msg");
        //3. 构建pojo，为了单独更新部分字段
        WmNews pojo = new WmNews();
        pojo.setId(newsId);
        pojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_FAIL);
        pojo.setReason(reason);
        //4. 更新
        updateById(pojo);

    }

    /**
     * 7.人工审核-通过
     *
     * @param paramMap
     */
    @Override
    public void authPass(Map<String, Long> paramMap) {
        //1. 获取文章id
        Long newsId = (Long) paramMap.get("id");
        //3. 构建pojo
        WmNews pojo = new WmNews();
        pojo.setId(newsId);
        pojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_MANUAL_PASS);
        //4. 更新
        updateById(pojo);

    }


    /**
     * 8.人工审核-查看文章详情
     *
     * @param newsId
     * @return
     */
    @Override
    public WmNewsVo getWmNewsVo(Long newsId) {
        WmNews wmNews = getById(newsId);
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        return WmNewsVo.build(wmNews,wmUser);
    }

    /**
     * 9.查询需要同步的文章status=8
     * @param dto
     * @return
     */
    @Override
    public List<WmNewsVo> list4ArticleSync(WmNewsPageReqDto dto) {
        //1. 构建条件status=8
        //2. 查询，得到数据列表，这个query调父类的方法。
        List<WmNews> wmNewsList = query().eq("status",dto.getStatus())
                //这一句为第10天优化
                .le(null!=dto.getEndPubDate(),"publish_time",dto.getEndPubDate()).list();

        //3. 获取所有作者wm_user_id
        List<WmNewsVo> voList = new ArrayList<>();
        // 自媒体账号id
        if (CollectionUtils.isNotEmpty(wmNewsList)){
            // 自媒体账号id
            List<Integer> wmUserIds = wmNewsList.stream().map(WmNews::getUserId)
                    .collect(Collectors.toList());
            //4. 批量查询wm_user表(ap_user_id作者id, name=作者名称)
            List<WmUser> wmUserList = wmUserMapper.selectBatchIds(wmUserIds);
            // map<userId,WmUser>其实是<key,value>，Function.identity()表示元素本身
            Map<Integer,WmUser> wmUserInfoMap = wmUserList.stream()
                    .collect(Collectors.toMap(WmUser::getId,Function.identity()));
            //5. 转成wmNewsVo
            voList = wmNewsList.stream().map(wmNews -> WmNewsVo
                    .build(wmNews,wmUserInfoMap.get(wmNews.getUserId())))
                    .collect(Collectors.toList()); //收集成list集合

        }
        //6. 返回
        return voList;
    }

    /**
     * 3.1删除文章与素材的关系
     * @param newsId
     */

    private void removeNewsMaterialRelationship(Long newsId) {
        LambdaQueryWrapper<WmNewsMaterial> queryWrapper = Wrappers.<WmNewsMaterial>lambdaQuery();
        queryWrapper.eq(WmNewsMaterial::getNewsId,newsId);
        wmNewsMaterialMapper.delete(queryWrapper);

    }

    /**
     * 2.1添加文章与图片的关系
     * @param newsId
     * @param coverImageList
     * @param contentImageList
     */

    private void saveWmNewsMaterialRelationship(Long newsId, List<String> coverImageList, List<String> contentImageList) {
        // 合并图片集合且去重
        Set<String> allImages = new HashSet<>();
        if (CollectionUtils.isNotEmpty(coverImageList)){
            // 去除集合中每个图片地址中的前缀
            coverImageList = coverImageList.stream().map(this::removePrefix).collect(Collectors.toList());
            allImages.addAll(coverImageList);
        }
        // 内容中的图片处理
        if (CollectionUtils.isNotEmpty(contentImageList)){
            // 图片地址截取去掉前缀，得到FileID
            contentImageList = contentImageList.stream().map(this::removePrefix).collect(Collectors.toList());
            allImages.addAll(contentImageList);
        }
        // 通过FileID查询素材表获取素材表中的id
        if (CollectionUtils.isNotEmpty(allImages)){
            // 构建批量查询条件
            LambdaQueryWrapper<WmMaterial> queryWrapper = Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl,allImages);
            // 通过url批量查询 allImages.size=3,  wmMaterials.size()=2
            List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(queryWrapper);
            if (null == wmMaterials || allImages.size() != wmMaterials.size()){
                throw new LeadNewsException("文章素材缺失");
            }
            // 添加素材(id,url)与文章的关系
            Map<String,Integer> urlIdMap = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl,WmMaterial::getId));
            // 相当于上面的一句话
            /*Map<String, Integer> map = new HashMap<>();
            for (WmMaterial wmMaterial : wmMaterials) {
                map.put(wmMaterial.getUrl(), wmMaterial.getId());
            }*/

            // map<url,id> map.get(url) -> id
            // 1. 封面 url, 转成WmNewsMaterial对象(newsId, 素材id,1)
            if (CollectionUtils.isNotEmpty(coverImageList)){
                for (String url : coverImageList) {
                    // 获取素材的id
                    Integer materialId = urlIdMap.get(url);
                    WmNewsMaterial newsMaterial = new WmNewsMaterial();
                    newsMaterial.setMaterialId(materialId);
                    newsMaterial.setNewsId(newsId);
                    newsMaterial.setType(BusinessConstants.WmNewsConstants.WM_COVER_REFERENCE);
                    wmNewsMaterialMapper.insert(newsMaterial);
                }
            }
            // 2. 内容 url 转成WmNewsMaterial对象(newsId, 素材id,0)
            if (CollectionUtils.isNotEmpty(contentImageList)){
                for (String url : contentImageList) {
                    // 获取素材的id
                    Integer materialId = urlIdMap.get(url);
                    WmNewsMaterial newsMaterial = new WmNewsMaterial();
                    newsMaterial.setMaterialId(materialId);
                    newsMaterial.setNewsId(newsId);
                    newsMaterial.setType(BusinessConstants.WmNewsConstants.WM_CONTENT_REFERENCE);
                    wmNewsMaterialMapper.insert(newsMaterial);
                }
            }
            //mybatis批量添加


        }

    }

    private String removePrefix(String url) {
        return url.replaceAll("http://((\\d){1,3}\\.){3}(\\d){1,3}:?(\\d){0,5}\\/","");
    }

    public static void main(String[] args) {
        String imageUrl = "http://192.168.211.128/group1/M00/00/00/wKjTgGJjqGmAZPsKAACWAYXFQc8970.jpg";
        Pattern p = Pattern.compile("http://((\\d){1,3}\\.){3}(\\d){1,3}:?(\\d){0,5}\\/");
        Matcher matcher = p.matcher(imageUrl);
        System.out.println(matcher.find());
        System.out.println(matcher.replaceAll(""));
    }


}

